home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 003 / db3n8510.arc / DB3N8510.TXT
Text File  |  1985-10-25  |  50KB  |  1,328 lines

  1. dBASE III Anomalies and Workarounds
  2.  
  3. 1.1  How to use this Section
  4.  
  5.  
  6. 1.2  @...GET alias to @...SAY...PICTURE
  7.  
  8. >>> @...GET alias in Format File
  9.  
  10. Any  command that  allows  editing of  a record  will  only  operate in the
  11. SELECTed work area.  However, attempting an @...SAY...GET using an ALIAS or
  12. SELECT to access another work area in a format (.FMT) file will not produce
  13. an error message. Instead, the entire line is suppressed, including the SAY
  14. statement.   The Developer's Release of dBASE III will return the "Variable
  15. not found" error message if any attempt is made to GET a field from another
  16. work area.  In versions 1.0 and 1.1 of dBASE III, the command:
  17.  
  18.      @ 10,4 SAY "Testing " GET B->Test
  19.  
  20. in a format  file  will  not display anything  on the  screen. Issuing this
  21. command from the dot prompt or in a command file will  return "Variable not
  22. found."  An alias name is acceptable in a  SAY statement,  even in a format
  23. file.
  24.  
  25.      @ 10,4 SAY "Testing " + B->Test
  26.  
  27. will display properly.
  28.  
  29. Notice that in the first example neither the SAY nor the  GET pertaining to
  30. the second work area are displayed.
  31. [1.0, 1.1, D.R.]
  32.  
  33.  
  34. >>> @...SAY...PICTURE "@("
  35.  
  36. Displaying negative numeric memory variables with  the @...SAY  command and
  37. the PICTURE function "@("  to enclose negative numbers in parentheses, will
  38. not  display  the  PICTURE  correctly.   Blanks  will  display  between the
  39. beginning parenthesis and the memory variable contents.  For example:
  40.  
  41.         number = 23.15
  42.         @ 10,10 SAY number PICTURE "@("
  43.  
  44.         (      23.15)
  45.  
  46. The workaround is as follows:
  47.  
  48.         STORE STR( number, 12, 2 ) TO mem1
  49.                          ^------------ Use an appropriate length
  50.                                        and decimal value.
  51.         STORE AT( "-", mem1 ) TO mpos
  52.         @ 10,10 SAY SPACE(mpos-1) + "(" + SUBSTR(mem1,mpos+1)  + ")"
  53.  
  54.         (23.15)
  55. [1.0, 1.1, D.R.]
  56.  
  57.  
  58. >>> @...SAY...PICTURE "$", ",", and "9"
  59.  
  60. Using the template symbols "$",  ",",  and "9" in an @...SAY PICTURE clause
  61. may  cause more  than  one dollar  sign  to  be  displayed  on  the screen.
  62. Specifically,  if the value being displayed does not fill  positions in the
  63. display  string  preceding  the  comma  position,  a  dollar  sign  will be
  64. displayed in place of the comma.
  65.  
  66. For example:
  67.  
  68.      mem = 123456
  69.      @ 10,0 SAY mem PICTURE "$999,999,999"
  70.      @ 11,0 SAY mem PICTURE "$999,999,999,999"
  71.  
  72. will output:
  73.  
  74.      $   $123,456
  75.      $   $   $123,456
  76.  
  77. The PICTURE template,  ($),  is intended  to  replace  leading  zeros  in a
  78. numeric display with  dollar  signs.   This means that  dollar signs should
  79. always  display in  a fixed position  format.   To display a fixed position
  80. dollar  sign  leading a numeric expression  with  embedded  commas, use two
  81. @...SAY commands,  one to display the dollar sign and the other  to display
  82. the numeric variable with the embedded commas.  For example,
  83.  
  84.      mem = 1234.56
  85.      @ 10,10 SAY "$"
  86.      @ ROW(),COL() + 1 SAY mem PICTURE "99,999.99"
  87.  
  88. If you wish a leading dollar  sign  that is floating  for numeric variables
  89. with embedded commas,  a feature not  directly supported by dBASE  III, use
  90. the following command file to format the numeric variables.
  91.  
  92.      * Commas.PRG
  93.      PARAMETERS mem
  94.      mvar = SUBSTR( STR( mem, 9, 2 ), 1, 3 ) + ",";
  95.           + SUBSTR( STR( mem, 9, 2 ), 4, 3 );
  96.           + SUBSTR( STR( mem, 9, 2 ), 7, 3 )
  97.      cntr = 1
  98.      DO WHILE SUBSTR( mvar, cntr, 1 ) $ ", "
  99.         cntr = cntr + 1
  100.      ENDDO
  101.      cnum = SPACE( cntr - 1 ) + "$" + SUBSTR( mvar, cntr )
  102.      @ 10,0 SAY cnum
  103.      * EOP Commas.PRG
  104.  
  105.      STORE 1234.56 TO x
  106.      DO Commas WITH x
  107.  
  108. outputs:
  109.  
  110.      $1,234.56
  111. [1.0, 1.1, D.R.]
  112.  
  113.  
  114.  
  115. This formula will work with numbers as large as $999,999.99. Larger numbers
  116. require that the length argument of the STR()  function and the  length and
  117. starting point arguments of the SUBSTR() function be changed to accommodate
  118. the larger number.  For numbers as large as $999,999,999.99 the second line
  119. of the preceding program should be changed to:
  120.  
  121.      mvar = SUBSTR( STR( mem, 12, 2 ), 1, 3 ) + ",";
  122.           + SUBSTR( STR( mem, 12, 2 ), 4, 3 ) + "," ;
  123.           + SUBSTR( STR( mem, 12, 2 ), 7, 3 ) +;
  124.             SUBSTR( STR( mem, 12, 2 ), 10, 2 )
  125.  
  126. This problem persists in the Developer's Release including both the PICTURE
  127. clause and the TRANSFORM()  function.   The  work-arounds are  a little bit
  128. easier given some new functions including TRANSFORM() itself.  To display a
  129. numeric  expression  with  a leading  dollar  sign  in  fixed  position and
  130. embedded commas concatenate a dollar sign to the result  of the TRANSFORM()
  131. of the numeric expression.  For example,
  132.  
  133.      mem = 1234.56
  134.      @ 10,10 SAY "$" + TRANSFORM( mem, "99,999.99" )
  135.  
  136. To display  a  floating  dollar  sign  leading  a  numeric  expression with
  137. embedded commas,  insert the dollar sign into the result of the TRANSFORM()
  138. function on the numeric expression.  For example,
  139.  
  140.      mem = 1234.56
  141.      @ 10,10 SAY SPACE(10-LEN(LTRIM(TRANSFORM(mem,"99,999.99"))));
  142.                         ^--------- Length of the TRANSFORM() picture.
  143.  
  144.                  + "$" + TRANSFORM(mem,"99,999.99")
  145.  
  146. It  is  important to  note that the first element in  the  SPACE() function
  147. argument must be the length of  the TRANSFORM()  picture.  If, for example,
  148. the picture is "999", then the element is three.
  149.  
  150.  
  151. >>> @...SAY...PICTURE "@X" and "@C"
  152.  
  153. The  C and X functions documented  in  the manual reference  to the @...SAY
  154. command will always display positive numbers as  credits (CR)  and negative
  155. numbers as debits (DB).  Use the following program segment if you need them
  156. to display in the reverse order (that  is,  positive numbers as debits (DB)
  157. and negative numbers as credits (CR)):
  158.      DO CASE
  159.         CASE number > 0
  160.            @ row,col SAY STR(number,17,2) + "DB"
  161.         CASE number < 0
  162.            @ row,col SAY STR(-number,17,2) + "CR"
  163.         OTHERWISE
  164.            @ row,col SAY STR(number,17,2)
  165.      ENDCASE
  166. [1.0, 1.1, D.R.]
  167.  
  168.  
  169. >>> @...SAY...PICTURE "@A" and "@!"
  170.  
  171. The A and !  PICTURE clause functions are mutually exclusive.  That is, you
  172. cannot combine the two to make a new function that will limit data input to
  173. alphabetic characters and force the letters to uppercase.   If A and !  are
  174. used together, only the second function will be in effect.  For example: 
  175.  
  176.      PICTURE "@A!"  is equivalent to   PICTURE "@!"
  177.      PICTURE "@!A"  is equivalent to   PICTURE "@A"
  178. [1.0, 1.1, D.R.]
  179.  
  180.  
  181. >>> @...SAY...PICTURE Logical Variable
  182.  
  183. There is no PICTURE clause that applies to a logical variable in an @...GET
  184. command.   However,  if you attempt to  use a PICTURE clause  on  a logical
  185. field or memory variable, dBASE III will not trap it as an error.  When the
  186. READ is executed, dBASE III will not pause for user input, and the value of
  187. the field or variable will not be changed.
  188.  
  189. One possible  occurrence  of  this  is  when a memory variable  is declared
  190. PUBLIC but is not initialized before  an @...GET command.   (dBASE III will
  191. automatically initialize a PUBLIC memory variable to a logical  .F.) If the
  192. variable was not intended to be a logical variable, the above symptoms will
  193. manifest themselves. You may want to use the SPACE() function to initialize
  194. character variables and zero to initialize numeric variables.
  195. [1.0, 1.1, D.R.]
  196.  
  197.  
  198. >>> @...SAY...PICTURE "@Z"
  199.  
  200. The zero-suppress function, @Z, will not suppress the decimal point when it
  201. is used  with  a non-integer value of  zero.   To work around  this display
  202. problem, use the following command sequence instead of the @Z function.
  203.  
  204.          IF STR( number, 5, 2 ) <> " 0.00"
  205.             @ 10,0 SAY number
  206.          ENDIF
  207. [1.0, 1.1]
  208.  
  209.  
  210. 1.3  APPEND to CONFIG.DB
  211.  
  212. >>> CONFIG.DB with SCOREBOARD
  213.  
  214. The entry SCOREBOARD=OFF  has no  effect  if  set in  CONFIG.DB. SETing the
  215. SCOREBOARD OFF suppresses the display of status and error messages  to line
  216. 0  .  The SCOREBOARD setting can only be changed from a command file or the
  217. dot prompt with the command SET SCOREBOARD OFF.
  218.  
  219.  
  220.  
  221.  
  222. 1.4  COPY [STRUCTURE] to DO WHILE
  223.  
  224.  
  225. 1.5  EDIT to LABEL limitations
  226.  
  227. >>> EDIT
  228.  
  229. If BROWSE is used  on an indexed file  and is  terminated with a  Ctrl-Q or
  230. Ctrl-W and followed  with  an EDIT command,  keys which  advance or regress
  231. through the database file (PgUp,  PgDn, uparrow, downarrow, Ctrl-E, Ctrl-R,
  232. Ctrl-C) will not work properly.
  233.  
  234. Sometimes these keys  will  drop  the user out  of EDIT to  the dot prompt.
  235. Other times they  will  move  two or  three records  instead  of  one.  The
  236. display will  sometimes lock  on the  current record.   These keys may also
  237. cause the pointer to be positioned at the next to last record in the index.
  238. [1.0, 1.1, D.R.]
  239.  
  240.  
  241. >>> INKEY()
  242.  
  243. The INKEY()  function of the Developer's  Release does not  always read the
  244. leftarrow key as CHR(19).   Running the following program  will demonstrate
  245. that dBASE III will trap this key only rarely.
  246.  
  247.     i = 0
  248.     DO WHILE i <> 13
  249.       i = INKEY()
  250.       ? i
  251.     ENDDO
  252.  
  253.  
  254.  
  255.  
  256. When you run this  program,  you will  notice  that  dBASE  III  treats the
  257. leftarrow key as a Ctrl-S,  and will pause or  start scrolling accordingly.
  258. Sometimes the key will be trapped and INKEY() will return 19; however, this
  259. is far less common than the former result.
  260. [D.R.]
  261.  
  262.  
  263. 1.6  MODIFY STRUCTURE to PCOL() and SET MARGIN TO
  264.  
  265.  
  266. 1.7  RECNO() to ROUND()
  267.  
  268.  
  269. 1.8  SET <full-screen> to SORT
  270.  
  271. >>> SET RELATION TO
  272.  
  273. The command syntax  SET RELATION  INTO  <file>  TO  <key>  will not SET the
  274. RELATION although no  error message is  displayed and DISPLAY  STATUS shows
  275. the RELATION as SET.
  276.  
  277. The SET RELATION command is sensitive to the order that the INTO and the TO
  278. clauses are specified.   Apparently,  everything  after the INTO  clause is
  279. ignored.  Be sure to check that you have specified the TO clause before the
  280. INTO clause if your RELATIONs appear to fail.
  281. [1.0, 1.1]
  282.  
  283.  
  284. >>> SORT
  285.  
  286. (1) Attempting to SORT a file with SET FILTER TO SUBSTR(fieldname,n,n) =
  287. <value> will return a "*** STACK OVERFLOW ***" and drop you out of dBASE
  288. III to the operating system.
  289.  
  290. (2) SORTing a database file from a command file that is nested 13 levels
  291. deep will fail with a system crash.  The SORT command will return a status
  292. message stating that a percentage of the records in the original file were
  293. SORTed, but then the computer will freeze with no error message or warning.
  294.  
  295. (3) Attempting to use the command SORT TO <Filename> ON <Fieldname> with a
  296. RELATION SET produces the error message "*** STACK OVERFLOW ***" and will
  297. drop you out of dBASE III to the operating system prompt.  This will not
  298. occur if the RELATION is SET to RECNO().
  299.  
  300. (4) One cannot SORT to a file in a directory other than the current one.
  301. An attempt to do this will not produce an error message and will not create
  302. the designated file.  Instead, a file name W44 will be created in the
  303. current directory of the default drive.
  304.  
  305. (5) The effective limit of SORT is in the 32,000 record range. SORTing a
  306. file with more than 32,000 records will produce the following error
  307. messages:
  308.  
  309.           nn% Sorted
  310.           Records do not balance...(PROGRAM ERROR)
  311.           100% Sorted
  312.  
  313. The SORTed file will not contain all the records from the original.
  314.  
  315. The workaround for sorting databases in excess of 32,000 records is to
  316. INDEX and then COPY.  The procedure is as follows:
  317.  
  318.           USE <filename>
  319.           INDEX ON <sort key> to Temp
  320.           COPY TO Sorted
  321. [1.0, 1.1]
  322.  
  323.  
  324. >>> SORT with 66 records
  325.  
  326. SORTing  a file  that  has more  than  66  records in  such a  way that the
  327. resulting  file  has exactly 64  records will  produce a file  in which the
  328. first record is corrupted.
  329. [1.0, 1.1]
  330.  
  331.  
  332. 1.9  STORE to ZAP
  333.  
  334. >>> USE
  335.  
  336. An attempt to use the PC/MS-DOS directory navigation path (..) in a USE
  337. command will give unexpected results.  USEing the database will make the
  338. file active, but there will be no ALIAS.  An attempt to USE another
  339. database file in another SELECT area using the same technique, will cause
  340. the error message "ALIAS name already in use" to be displayed.
  341.  
  342.      * ---Open first database file.
  343.      USE ..\nme\Budget
  344.      DISPLAY STATUS
  345.      
  346.      Currently selected database:
  347.      Select area - 1, Database in use: C:..\nme\Budget.dbf
  348.        Alias -
  349.  
  350.      * ---Open another database file.
  351.      SELECT 2
  352.      USE ..\nme\Sumfrm
  353.      ALIAS name already in use
  354.                 ?
  355.      USE ..\nme\sumfrm
  356.  
  357. If an ALIAS is assigned in both USE commands, no error results. If you need
  358. to avoid specifying the full path name, use the RUN command to log the
  359. previous directory and then specify the datafile to USE.  For example,
  360.  
  361.      RUN CD ..
  362.      USE nme\Budget
  363.      SELECT 2
  364.      USE nme\Sumfrm
  365. [1.0, 1.1, D.R]
  366.  
  367.  
  368. 1  dBASE III Programming
  369.  
  370. 1.1   Program documentation
  371.  
  372.  
  373. 1.2   Database file structure
  374.  
  375.  
  376. 1.3   Debugging - program break points
  377.  
  378.  
  379. 1.4   dFORMAT
  380.  
  381.  
  382. 1.5   MEMO fields
  383.  
  384.  
  385. 1.6   Setting the system date and time
  386.  
  387.  
  388. 1.7   Recreating a corrupted dBASE III header
  389.  
  390.  
  391. 1.8   Simulating the JOIN command
  392.  
  393.  
  394. 1.9   Limitations
  395.  
  396.  
  397. 1.10  Capatilizing
  398.  
  399.  
  400. 1.11  Left-justifying Character Fields
  401.  
  402.  
  403. 2  dBASE III Frequently Asked Questions
  404.  
  405. 2.1  Installation
  406.  
  407.  
  408. 2.2  Commands
  409.  
  410.  
  411. 2.3  New data types
  412.  
  413.  
  414. 2.4  Memory variables
  415.  
  416.  
  417. 2.5  Printing
  418.  
  419.  
  420. 2.6  Data transfer
  421.  
  422.  
  423. 3  dBASE III Reference
  424.  
  425. 3.1   @...GET to Boolean Operators
  426.  
  427. >>> @...GET PICTURE "@B"
  428.  
  429. The function "@B"  is used with the @...SAY...GET statement to left-justify
  430. variables of numeric type.   It is most useful when SAYing a  numeric field
  431. or memory variable that  is more easily  understood as a  character string,
  432. such as a part number.   Use of this FUNCTION  with GET,  however, causes a
  433. slight change in the way numeric variables are read  from  the  screen that
  434. may cause some difficulties.
  435.  
  436. A  numeric memory  variable  will  default to  a length of ten  digits when
  437. initialized;  however,  if you are using the PICTURE  function  "@B"  in an
  438. @...GET  statement,  a numeric memory  variable  will GET the  width of the
  439. memory variable exactly as initialized,  even if a template symbol is used.
  440. Initializing a memory  variable  to  zero  will cause the  GET statement to
  441. display one zero on the screen.   A READ command will allow  one digit only
  442. to be entered to the memory variable.
  443.  
  444. This occurs whether the memory variable is  initialized to 0  or 0000.  For
  445. example:
  446.  
  447.      SET DELIMITERS ON
  448.      w = 1234
  449.      x = 0
  450.      y = 0000
  451.      @  9,0 GET w PICTURE "@B9999"
  452.      @ 10,0 GET x PICTURE "@B9999"
  453.      @ 11,0 GET y PICTURE "@B9999"
  454.  
  455.  
  456. will produce:
  457.  
  458.      :1234:
  459.      :0:
  460.      :0:
  461.  
  462. A READ  command will  allow four  characters  to  be  entered in  the first
  463. variable,  but only one character in the  next two variables.   If the "@B"
  464. function is used,  initialize the memory  variable to the proper  length or
  465. the input may be truncated.
  466.  
  467.  
  468.  
  469.  
  470. 3.2   CHR() to FILE() function
  471.  
  472. >>> Demonstration Disk (Runtime+)
  473.  
  474. In  the Developer's Release of  dBASE  III,  the  dBRUN  programs  from the
  475. Demonstration Disk are not compatible with the full system.
  476.  
  477. Code crunched with DBC.COM from the Demonstration Disk can only be run with
  478. dBRUN  from  the Demonstration Disk.   Code crunched with  DBC.COM from the
  479. Developer's  Disk  can only be run with  the full  dBASE III system  or the
  480. dBRUN disk, purchased separately.
  481.  
  482. The error message:
  483.  
  484.      No database is in USE.  Enter filename:
  485.  
  486. is a common indicator that the incorrect dBRUN or dBC is being used.
  487.  
  488.  
  489. 3.3   FIND to MODIFY STRUCTURE
  490.  
  491.  
  492. 3.4   Numeric fields to PARAMETERS
  493.  
  494.  
  495. 3.5   PRIVATE to PROW()
  496.  
  497. >>> PROW()
  498.  
  499. When issuing SET PRINT ON,  PROW()  will be  set to  2,  regardless  of its
  500. previous setting.  An EJECT will reset PROW() to 2, not 0. Issuing an EJECT
  501. with SET PRINT OFF will reset PROW() to 0.
  502.  
  503. When SET PRINT is ON,  PROW()  may never equal 0 or 1.  Any attempt to test
  504. for PROW() = 0 or PROW() = 1 will work only when SET PRINT is OFF.
  505.  
  506.  
  507.  
  508. 3.6   PUBLIC to REPORT FORM
  509.  
  510.  
  511. 3.7   RELEASE to SET PROCEDURE
  512.  
  513. >>> SET COLOR TO  ,<cr>
  514.  
  515. The command:
  516.  
  517.      SET COLOR TO  ,<cr>
  518.  
  519. will produce black on black, even if there is no space after the comma.
  520.  
  521.  
  522.  
  523.  
  524. 3.8   SET RELATION to warnings
  525.  
  526.  
  527. 3.9   dBase III File Structure
  528.  
  529.  
  530. 3.10  dBASE III Memo File Structure
  531.  
  532.  
  533. 3.11  Installation and Configuration
  534.  
  535.  
  536. 4  dBASE III Sample Programs
  537.  
  538.  4.1  Left Justifying Character Fields
  539.  
  540.       by Stanley Ballenger
  541.  
  542.  Converting a numeric field to a character field using MODIFY STRUCTURE will
  543.  leave the character field right-justified.   If you find that you  need the
  544.  new character field to be left-justified, the following procedure will be a
  545.  welcome addition to your procedure library.
  546.  
  547.  This procedure takes  any  database  file  and  left-justifies  all  of the
  548.  character fields in each record,  trimming all the leading blanks.  It uses
  549.  an EXTENDED STRUCTURE to hold the names of all the character fields.  Then,
  550.  taking each character field in turn,  it passes through the entire database
  551.  file left-justifying that  character field's  values.   The  next character
  552.  field  is  read  from the EXTENDED STRUCTURE and the  process  is repeated.
  553.  This proceeds  until there are no  more  character fields  to left-justify.
  554.  Note that this procedure makes as many passes through your database file as
  555.  there are character fields and, as such executes quite slowly.
  556.  
  557.  * Program....: Ljustify.PRG
  558.  * Author.....: Stanley Ballenger
  559.  * Date. .....: July 1, 1985
  560.  * Notes......: Left-justifies all character type fields in a
  561.  *              database file.
  562.  *
  563.  PRIVATE start, end, string, fname
  564.  * ---Open files.
  565.  SELECT 1
  566.  USE Yourfile
  567.  COPY STRUCTURE EXTENDED TO Temp
  568.  SELECT 2
  569.  USE Temp
  570.  SET FILTER TO Field_type = "C"
  571.  GO TOP
  572.  CLEAR
  573.  * ---Set up field count and display it.
  574.  fldcount = 0
  575.  column = COL() + 10
  576.  @ ROW(),column SAY STR( fldcount,10 ) + " Field values replaced"
  577.  * ---Justify character fields.
  578.  DO WHILE .NOT. EOF()
  579.  fname = Field_name
  580.     SELECT Yourfile
  581.  GO TOP
  582.     end = LEN( &fname )
  583.  * ---Remove leading blanks for the current field.
  584.     DO WHILE .NOT. EOF()
  585.        start = 1
  586.        string = &fname
  587.  * ---Remove leading blanks from cu~rrrent field.
  588.        DO WHILE SUBSTR( string,start,1 ) = " " .AND. start < end
  589.           start = start + 1
  590.        ENDDO
  591.   * ---Replace field if it is left-justified.
  592.  IF start <> 1
  593.  REPLACE &fname WITH SUBSTR( string,start,end )
  594.  fldcount = fldcount + 1
  595.  @ ROW(),c~orlumn SAY STR( fldcount,10 )
  596.  ENDIF
  597.  * ---Get next record.
  598.        SKIP
  599.     ENDDO
  600.     SELECT Temp
  601.  * ---Get next character field name.
  602.     SKIP
  603.  ENDDO
  604.  * ---Clean up.
  605.  CLOSE DATABASES
  606.  ERASE Temp.DBF
  607.  RETURN
  608.  * EOF Ljustify.PRG
  609.  
  610.  
  611.  4.2  Creative Uses of Database Files for Reporting
  612.  
  613.  Database files are central  to  dBASE  II  and  dBASE  III.   They  are the
  614.  repositories of the information we want to record and  access. Typical uses
  615.  of database  files are mailing lists,  customer lists,  records of purchase
  616.  requisitions, or accounts receivable and payable.  These will always be the
  617.  most important uses for database files.
  618.  
  619.  However,  there are other ways  to use database files that  may seem  a bit
  620.  more exotic at first, but they can be very powerful.  The following program
  621.  shows how a database  file  can be used to  supply a  program with variable
  622.  parameters.   In this case,  the database file Conditn.DBF holds the number
  623.  of different conditions on which the  program is REPORTing,  sparing us the
  624.  necessity of hard-coding the conditions into the program  itself.  If later
  625.  we want to modify or add conditions, we need only modify Conditn.DBF.
  626.  
  627.  This program performs the following function.   It reports from  the log of
  628.  all the support calls that I have received in the last two  weeks, breaking
  629.  them down  by  product (dBASE II,  dBASE III,  Framework, Friday, and other
  630.  utility programs).   It uses REPORT FORM for its ease,  but  also does some
  631.  statistical  reporting that  the  REPORT  FORM  cannot  perform.   Since my
  632.  database file  has no  numeric  fields,  I  have  no  need  for  totals and
  633.  subtotals.  However, I do want to know the count of calls for each product.
  634.  The program uses EJECT, NOEJECT, PROW(), and PCOL() to position the printer
  635.  correctly.
  636.  
  637.  The structure for the database file Conditn.DBF is as follows:
  638.  
  639.  Structure for database file: Conditn.DBF
  640.  Field  Field Name  Type       Width    Dec
  641.      1  Condition   Character     15
  642.  ** Total **                      16
  643.  
  644.  
  645.  
  646.  It has one record for each product:
  647.  
  648.       Product = 'D3'
  649.       Product = 'D2'
  650.       Product = 'FW'
  651.       Product = 'FR'
  652.       Product = 'UT'
  653.  
  654.  for a total of five records.
  655.  
  656.  * Program ..: Cumprint.PRG
  657.  * Author ...: Ralph Davis
  658.  * Date .....: October 1,1985
  659.  * Note .....: Prints counts from cumulative call log.
  660.  
  661.  SET TALK OFF
  662.  ACCEPT " FILENAME:  " TO filename
  663.  ACCEPT "FORM NAME:  " TO formname
  664.  USE &filename
  665.  SELECT 2
  666.  USE Conditn
  667.  * ---EJECT page to reset line and column counters.
  668.  SET PRINT ON
  669.  EJECT
  670.  * ---Initialize control variables.
  671.  printmore = .T.
  672.  memcond = Condition
  673.  DO WHILE printmore
  674.     SELECT &filename
  675.     COUNT FOR &memcond TO mcount
  676.     IF mcount > 0
  677.       REPORT FORM &formname NOEJECT TO PRINT FOR &memcond
  678.       SET DEVICE TO PRINT
  679.       * ---Skip to next page if line counter is past 58.
  680.       IF PROW() > 58
  681.         @ 5,0
  682.       ENDIF
  683.       @ PROW()+2,1 SAY "NUMBER OF CALLS:  "
  684.       @ PROW(),PCOL()+1 SAY mcount
  685.       SET DEVICE TO SCREEN
  686.       EJECT
  687.     ENDIF
  688.     * ---Return to control file.
  689.     SELECT Conditn
  690.     * ---Get next condition.
  691.     SKIP
  692.     * ---Leave program if no more conditions.
  693.     IF EOF()
  694.        EXIT
  695.     ELSE
  696.        memcond = Condition
  697.     ENDIF
  698.  ENDDO
  699.  SET PRINT OFF
  700.  SET TALK ON
  701.  RETURN
  702.  * EOP Cumprint.PRG
  703.  
  704.  
  705. 5  dBASE III Technical Notes
  706.  
  707. 5.1  Simple List Program
  708.  
  709. dBASE III offers standard list reporting through the  REPORT  FORM command.
  710. The  REPORT  FORM is easy  to learn  and very useful for  column formatting
  711. whether  you use one file  or  several files that  are linked  with the SET
  712. RELATION command.  Often, however, you may find the REPORT FORM restrictive
  713. and so find it necessary to design a custom report.
  714.  
  715. There are two fundamental reasons  for  this.   First,  there  are physical
  716. limitations to the REPORT FORM  definition.  Specifically, there are limits
  717. to the number of fields and characters in a report definition.  If you have
  718. a complex or  large report,  you may  have run into these  limits.  Second,
  719. your report requires  features that are not supported by  the  REPORT FORM.
  720. These may include  statistical  calculations  such  as  averaging, multiple
  721. parent-child relations,  formatted numeric fields, and non-columnar format,
  722. such as a pre-printed form.In all of  these cases a custom  report  must be
  723. written.
  724.  
  725. The purpose of this article is to help  you understand  the concepts behind
  726. writing a custom report by way of a working example.   The following sample
  727. program illustrates a method  to  report  on  two files  with  one  to many
  728. relationships.
  729.  
  730. The program was originally written for  a school that employs  sales people
  731. to recruit students.   It generates a report that details all  the students
  732. recruited for each recruiter and the current session.   There are two files
  733. used.   One is  Recruit.DBF and the other is  Referral.DBF.   The two files
  734. contain a common field,  Ref_code, a unique code assigned to each recuiter.
  735. In the Referral.DBF file,  Ref_code indicates which recuiter signed up that
  736. particular  referral.   For each  record in the Recruit file  there  may be
  737. several associated records in the Referral file.
  738.  
  739. The Recruit database file contains information regarding  the sales people:
  740. name,  address,  city, state, zip, and the unique referral code (Ref_code).
  741. The Referral  database file contains  student and class  information: name,
  742. school location,  class level, and referral code (Ref_code), and is INDEXed
  743. ON Ref_code TO  Code.NDX.   As with any program,  the first part  sets up a
  744. specific environment.  SET defaults are changed, the screen is CLEARed, the
  745. necessary files are opened,  and the  printer is accessed.   All the memory
  746. variables utilized  globally by the program are then  initialized.   A page
  747. counter is set up (m_pagectr)  and a line counter is  established (m_line).
  748. The line  counter,  m_line,  is initialized to  a large number  so that the
  749. first page is EJECTed. Next, a loop is established to instruct dBASE III to
  750. process the commands until there are no more records  in Recruit.DBF.  This
  751. is indicated when the EOF()  function returns a true value.  The idea is to
  752. use Recruit.DBF as the point of reference into the Referral.DBF.
  753.  
  754. The next step is to find  and print all the records  from  the Referral.DBF
  755. file,  that have  the same Ref_code as  the current record  in Recruit.DBF.
  756. This is done  by SELECTing  the second file and  SEEKing Recruit->Ref_code.
  757. Note  the  use  of  the  alias  name  when  SEEKing  with  a  field  form a
  758. non-SELECTed work area.
  759.  
  760. A test must then be made to  determine if  there are any referrals  for the
  761. current recruiter.  The statement IF EOF() tests for this.  If there are no
  762. referrals found by the SEEK command,  EOF()  returns a true (.T.).  In this
  763. example,  if a record  was not found,  dBASE III  is  instructed  to SELECT
  764. Recruit.DBF,  SKIP to the  next record,  LOOP around to the  DO WHILE .NOT.
  765. EOF() statement, and start the process again.
  766.  
  767. If a record is found,  then the record  pointer is positioned at  the first
  768. occurrence  of  Ref_code  in  Referral.DBF.   In  order to  display all the
  769. records that have the same referral code, it is necessary to enter a second
  770. DO  WHILE loop  that will skip  through Referral.DBF until  the Ref_code in
  771. this file no longer matches Recruit->Ref_code,  or  the end of  the file is
  772. reached.
  773.  
  774. The statement "IF m_line >  56" tests the line counter to determine whether
  775. or not to eject to a new page before printing.  This is where the statement
  776. "m_line = 90" makes sense.  Storing 90 to m_line every time a new record is
  777. selected from the Recruit file ensures that the first time the statement IF
  778. m_line >  56 is evaluated, headings will be printed since m_line is greater
  779. than 56.   Notice that right after that IF statement m_line is reset to 11,
  780. the location where the first line of data will be printed.
  781.  
  782. After the ENDIF,  the first record  from  the Referral.DBF  is printed, the
  783. line  counter is  incremented,  and the record  pointer SKIPs  to  the next
  784. record.   It is necessary to increment the line counter so that the printer
  785. moves forward.   When sending data  to  the printer  for  a  custom report,
  786. @...SAYs should move from top to bottom and left  to right.   If an attempt
  787. is made to print to a row and column position that has already been printed
  788. on  the current page,  then a formfeed  is  sent  to  the printer.   In our
  789. example,  we use this concept to our advantage.  The headings are set up to
  790. begin printing on line 1, which requires the printer to eject to the top of
  791. a new page if it isn't already positioned there.
  792.  
  793. dBASE III stays in this loop until  all the records from  Referral.DBF that
  794. match Recruit->Ref_code are printed.   Each time  a  new  record  is  to be
  795. printed, m_line is tested, and ejects to a new page when appropriate.
  796.  
  797. Once all the matched records are  printed  from  Referral.DBF,  the program
  798. SELECTs the Recruit.DBF file,  moves the record pointer to the next record,
  799. and loops around to the  first DO WHILE  .NOT.  EOF() to begin processing a
  800. new record.
  801.  
  802. When all the records in Recruit.DBF have been reported on, an EJECT is sent
  803. to  be  sure  that the last line  of data is  printed.  The printer is then
  804. disabled  (SET  DEVICE  TO  SCREEN),  the files are closed,  and control is
  805. RETURNed to the calling program or the dot prompt.
  806.  
  807. To  test  this  program,  add two  or  three  records  to  Recruit.DBF with
  808. Ref_codes 101  and 102.  Then add a few records to Referral.DBF and include
  809. the same Ref_codes,  101 and 102.  Once this is done, INDEX Referral.DBF ON
  810. Ref_code TO Code.NDX.  To execute the program, turn the printer on and type
  811. DO Report.
  812.  
  813.  
  814. Pseudocode
  815.  
  816. 1. Set Up the Working Environment.
  817.     Clear the screen.
  818.     Define environmental SETtings.
  819.     Define work areas.
  820.         Open database files with appropriate indices.
  821.         Define relations and filters.
  822.         Select the initial work area.
  823.     Initialize global variables.
  824.         Set the page counter to 0.
  825.         Set the line counter to a value greater than the page length.
  826.     Direct output to the printer.
  827. 2. Print the Report.
  828.     Process all the records in the primary work area.
  829.         Select the secondary work area.
  830.             Move to the first secondary record.
  831.     If there aren't any.
  832.             Select the primary work area.
  833.             Move to the next primary record.
  834.             Start the process over.
  835.           Process all the records in the secondary work area that match the
  836.           primary record.
  837.             If the line counter is greater than the page length
  838.                 Advance the printer to a new page.
  839.                 Print page heading.
  840.                 Print page number and date.
  841.                 Print report title.
  842.                 Print field titles.
  843.                 Increment the page counter.
  844.                Set the line count to the row position  of  the first detail
  845.                line.
  846.             Print one detail line.
  847.             Increment the line count.
  848.             Move to next secondary record.
  849.         Select the primary area
  850.         Move to next primary record.
  851. 3. Restore the Environment.
  852.     Advance the printer to a new page.
  853.     Direct output to the screen.
  854.     Close all the work areas.
  855.     Return to calling program or the dot prompt.
  856.  
  857. Report
  858.  
  859. The following command file was built from the pseudocode listed above.
  860.  
  861. * Program..: Report.PRG
  862. * Author...: Karen A. Robinson
  863. * Date.....: October 1, 1985
  864. * Notes....: USEs Referral.DBF and Recruit.DBF to generate a
  865. *            custom report of student referrals by recruiter.
  866. *
  867. CLEAR
  868. SET TALK OFF
  869. * --- Open work areas.
  870. SELECT 2
  871. USE Referral INDEX Code
  872. SELECT 1
  873. USE Recruit
  874. SET DEVICE TO PRINT
  875. * --- Initialize memory variables for the page headings.
  876. STORE 1 TO m_pagctr
  877. STORE 90 TO m_line
  878. * --- Print all recruiters and their respective referrals.
  879. DO WHILE .NOT. EOF()
  880.    * --- Calculate column positions so that the name and address
  881.    * --- of the recruiter will be centered.
  882.    STORE 40 - LEN(TRIM(Name))/2    TO m_1
  883.    STORE 40 - LEN(TRIM(Address))/2 TO m_2
  884.    STORE 40 - ((LEN(TRIM(City)) + LEN(State) + LEN(Zip) + 4)/2);
  885.          TO m_3
  886.    * -- Find the first referral from Recruit.DBF.
  887.    SELECT Referral
  888.    SEEK Recruit->Ref_code
  889.    * --- Test for the existence of a referral.
  890.    * --- If one is not found, then get the next recruiter
  891.    * --- and start the process over.
  892.    IF EOF()
  893.       SELECT Recruit
  894.       SKIP
  895.       LOOP
  896.    ENDIF
  897.  
  898.    * --- The first referral has been found.  The following
  899.    * --- prints all the records from Referral.DBF that have
  900.    * --- the same Ref_code as the Ref_code in Recruit.DBF.
  901.    DO WHILE Ref_code = Recruit->Ref_code .AND. .NOT. EOF()
  902.       * --- Test for a new page.  If the line count exceeds
  903.       * --- 56, a new page is required.
  904.       IF m_line > 56
  905.          * --- Print page and column headings.
  906.          @  1, 28 SAY 'STUDENT REFERRAL REPORT'
  907.          @  1, 65 SAY 'Page ' + STR(m_pagctr,3)
  908.          @  2, 65 SAY 'Date    '
  909.          @  2, 72 SAY DATE()
  910.          @  4,m_1 SAY Recruit->Name
  911.          @  5,m_2 SAY Recruit->Address
  912.          @  6,m_3 SAY TRIM(Recruit->City) + ', ' +;
  913.                       Recruit->State + ' ' + Recruit->Zip
  914.          @  9,14 SAY 'Level'
  915.          @  9,23 SAY 'Center'
  916.          @  9,43 SAY 'Student Name'
  917.          * --- Increment page and line counts.
  918.          STORE 11 TO m_line
  919.          STORE m_pagctr + 1 TO m_pagctr
  920.       ENDIF
  921.       * --- Print a referral, a record from the Referral
  922.       * --- database file.
  923.       @ m_line,14 SAY Level
  924.       @ m_line,23 SAY Center
  925.       @ m_line,43 SAY Name
  926.       * --- Increment the line counter.
  927.       STORE m_line + 1 TO m_line
  928.       * --- Get the next Referral for the current
  929.       * --- recruiter.
  930.       SKIP
  931.    ENDDO
  932.    * --- Get the next recruiter.
  933.    SELECT Recruit
  934.    SKIP
  935. ENDDO
  936. * --- Restore the environment and return.
  937. EJECT
  938. SET DEVICE TO SCREEN
  939. CLOSE DATABASES
  940. RETURN
  941. * EOF Report.PRG
  942.  
  943. Conclusion
  944.  
  945. In this article,  we have discussed custom reporting using Report.PRG as an
  946. example.    To  recapitulate,   Report.PRG   utilizies   several  reporting
  947. techniques.   First,  the program reports data from two database files that
  948. have a common field, Ref_code. Second, a page header is centered on the top
  949. of each page.  Third, a line counter is incremented to a large number which
  950. causes the formatted report  to  print on  a separate page for  each record
  951. that meets a sepcified condition.   You  can change Report.PRG to  fit your
  952. needs, replacing filenames and field names with those of your own.
  953.  
  954.  
  955.  5.2  Passing Parameters to Assembly-language Subroutines
  956.  
  957.     by Ralph Davis
  958.  
  959.  General Considerations
  960.  
  961.  With the  introduction of the Developer's Release,  dBASE III  has acquired
  962.  the ability  to  pass  parameters  to  assembly  language  subroutines.  In
  963.  combination with the ability  to LOAD and CALL  the routines,  you now have
  964.  the possibility of  much  closer  linking between a  dBASE  program  and an
  965.  assembler routine than was previously possible using the RUN command.
  966.  
  967.  The dBASE III Developer's  Release Reference Manual  documents this feature
  968.  on pages 4-26A and 4-72A through 72C.   It states that the address at which
  969.  the variable is stored is passed in  DS:BX;  that you can pass any  type of
  970.  memory variable; that character variables terminate with the null character
  971.  (ASCII zero);  and that you must not shorten or lengthen the variables that
  972.  you pass.
  973.  
  974.  In this article,  I will explore some techniques for  passing parameters to
  975.  assembler routines from the Developer's Release of dBASE III, and make some
  976.  suggestions for the most efficient ways to do this.
  977.  
  978.  
  979.  How to Pass Variables
  980.  
  981.  One thing you will need to understand is  the information dBASE  III passes
  982.  you.   What form are the different variables in? What can you do with them?
  983.  What are the best ways to do so?
  984.  
  985.  First of all, it is interesting to note that DS:BX points to the first byte
  986.  of  data,  not  the first byte  of  the variable.  The  first  byte  of all
  987.  variables contains the length of the  variable.  You can obtain this easily
  988.  by decrementing BX.   For character variables, the length includes the null
  989.  character string  terminator  at  the end;  therefore,  the length  you are
  990.  concerned with  is  the length  descriptor  minus one.   All other variable
  991.  types--numeric,  date,  and logical--have a fixed  length  of  eight bytes.
  992.  Each one has a different format in memory. 
  993.  
  994.  Secondly,  although  it appears  that you can  only pass one variable  at a
  995.  time,  if you are methodical,  you can pass as many variables  as you want.
  996.  As you  might  expect,  variables  are  assigned  to  memory  storage space
  997.  sequentially.   As long  as  they  are not RELEASEd,  and  the memory space
  998.  reassigned,  they will be stored one right after the other.   Therefore, by
  999.  passing the first variable,  you can easily locate all subsequent variables
  1000.  by using the length  descriptors.   Later in this  article,  I will go into
  1001.  greater detail about how to do this.
  1002.  
  1003.  
  1004.  Format of Variables in Memory
  1005.  
  1006.  Memory variables are stored in the following formats:
  1007.  
  1008.       1.  Character variables are stored with the length in  the first byte,
  1009.          followed by the actual string, and ending with 00.
  1010.  
  1011.       2.  Numeric variables are stored  in  IEEE long-real  format. They use
  1012.          eight bytes,  which appear in reverse order  (most significant byte
  1013.          at the highest memory address).   The first bit is the sign  bit: 0
  1014.          for positive and 1 for negative.  The next 11 bits are the exponent
  1015.          (that is,  the power of 2) plus 1023 decimal (3FF hexadecimal). The
  1016.          final 52  bits are the mantissa,  the part of  the number following
  1017.          the decimal point.   The actual value of the number  is computed as
  1018.          1.<mantissa> times 2 to the exponent-1023).  For instance, 2 (1.000
  1019.          times 2^1) is stored as:
  1020.  
  1021.            00 00 00 00 00 00 00 40
  1022.  
  1023.          Negative four, -4, (-1.000 times 2^2) is stored as:
  1024.  
  1025.            00 00 00 00 00 00 10 C0
  1026.  
  1027.          Notice that the bits of the mantissa  represent negative  powers of
  1028.          2,  from  2^(-1)  (1/2)  to 2^(-52) (approximately 1 divided by 4.5
  1029.          quadrillion).
  1030.  
  1031.       3.  Logical variables are stored as eight bytes, apparently to reserve
  1032.          space for redefining  them.   All  PUBLIC  variables  are initially
  1033.          defined as logical type with a value of false until they  are given
  1034.          a value.  The first byte is 01 for .T.  and 00 for .F.  This is the
  1035.          only byte you need be concerned with.
  1036.  
  1037.       4.  Date variables are also  stored  in  reverse  order  MSB  last) in
  1038.          encoded binary form.
  1039.  
  1040.  
  1041.  Suggestions
  1042.  
  1043.  Passing Multiple Variables
  1044.  
  1045.  When you pass a variable to an assembly  language subroutine,  you do so by
  1046.  issuing the command:
  1047.  
  1048.       CALL <routine> WITH <variable>
  1049.  
  1050.  For instance, if you store 'JOHN Q. PUBLIC' to mem1 and then CALL a routine
  1051.  with mem1, when your routine gains control, DS:BX will be pointing to the J
  1052.  in JOHN.   Decrement BX to retrieve the length descriptor,  in this case 15
  1053.  (0F hex).  To re-emphasize: the length includes the 00 which terminates all
  1054.  string variables.  The actual string is only 14 characters long.
  1055.  
  1056.  If you initialize  several memory  variables at  once,  then CALL a routine
  1057.  with the first one,  you can use the length  descriptor to locate  the next
  1058.  one.   If, after storing 'JOHN Q.  PUBLIC' to mem1, you store 'LOS ANGELES'
  1059.  to mem2  and 'CA'  to mem3,  you can pass mem1  to your routine, and locate
  1060.  mem2  and mem3 in the following manner.  Decrement BX to get mem1's length.
  1061.  Add it to  BX.   Add one to BX for the terminating descriptor.   Now  BX is
  1062.  pointing to  mem2's length descriptor.   Add this  value to BX and  add one
  1063.  again;  you are now pointing to mem3's length descriptor.  To pass multiple
  1064.  variables  in  this  fashion,  it is critical that you initialize  them one
  1065.  right after the other.  For example:
  1066.  
  1067.       STORE 'JOHN Q. PUBLIC' TO mem1
  1068.       STORE 'LOS ANGELES' TO mem2
  1069.       STORE 'CA' TO mem3
  1070.  
  1071.  Then you can CALL your routine and easily locate mem1, mem2, and mem3.  If,
  1072.  however,  you RELEASE and then redefine a variable, you will not be able to
  1073.  do this.  The following sequence will not work:
  1074.  
  1075.       STORE 'JOHN Q. PUBLIC' TO mem1
  1076.      STORE 'LOS ANGELES' TO mem2
  1077.  
  1078.      ...        <other commands not affecting memory variables>
  1079.  
  1080.      RELEASE mem1
  1081.      STORE 'CA' TO mem1
  1082.  
  1083.  I therefore suggest that when you pass variables to subroutines,  STORE all
  1084.  of them immediately prior to invoking the routine.   Even if  the variables
  1085.  have  been  defined elsewhere,  redefine  them  here with new  names.  Then
  1086.  replace the old variables upon returning from the routine.   This will give
  1087.  you the greatest  degree  of  control over  the variables you pass  to your
  1088.  routine.
  1089.  
  1090.  
  1091.  Passing Different Variable Types
  1092.  
  1093.  As I mentioned  earlier,  numeric variables are stored in IEEE  format, and
  1094.  dates are stored  in  an encrypted binary form.  Both of  these formats are
  1095.  cumbersome to work with.  So I recommend that you pass all date and numeric
  1096.  variables as characters.  There will undoubtedly be applications  where you
  1097.  will want the actual binary representation of numeric or date information--
  1098.  ones  using the 8087  or  80287,  for example--and  in  these instances, of
  1099.  course, it is appropriate to pass the variable in its actual form.  But for
  1100.  most purposes, it is much easier to pass the ASCII codes, then process them
  1101.  as needed in your routine.   Since the only part of a logical variable that
  1102.  concerns  us  is the  first byte,  it is best to  pass it as  is.  You then
  1103.  return 0  for false and 1 for true.  The program Printchk.ASM, listed later
  1104.  in  this  issue,  passes a logical variable,  and determines program action
  1105.  based on the value returned by the assembler routine.
  1106.  
  1107.  
  1108. Checking Printer Status
  1109.  
  1110.    by Ralph Davis
  1111.  
  1112. Introduction
  1113.  
  1114. We are all human, and need not be ashamed to admit that sometimes we try to
  1115. print files when our printer is either turned off or  not ready.   If we do
  1116. this from dBASE II or III,  we may soon be facing the DOS  prompt.  Neither
  1117. dBASE II  nor III checks  the printer before  trying  to  print.  The error
  1118. recovery  is  left  to  DOS.   If the printer is  off-line,  DOS issues the
  1119. familiar  "Write  fault error  writing  device  PRN  Abort,  Retry, Ignore"
  1120. message.  Turning the printer on-line and pressing "R"  for "Retry" resumes
  1121. the printing operation,  and all goes well.   Pressing "A" or "I," however,
  1122. returns us  to  the operating system  prompt,  and may  cause database file
  1123. corruption.
  1124.  
  1125. The  following assembler routine,  Prntchk.ASM,  checks  the printer status
  1126. port for LPT1  and controls program flow according to its  findings.  It is
  1127. written according to the specifications outlined in the  September issue of
  1128. TechNotes,  with conditional assembler directives to permit the same source
  1129. code to  create  programs either for dBASE II,  dBASE  III 1.0  and 1.1, or
  1130. dBASE III Developer's Release.   It functions somewhat differently  in each
  1131. environment.
  1132.  
  1133. The program is called Prntchk rather than Printchk because of an anomaly in
  1134. the Developer's Release with the LOAD command which obliges us to name LOAD
  1135. modules  with  seven characters  or  less.  Keep this  in  mind when naming
  1136. programs for use with dBASE III.
  1137.  
  1138.  
  1139. Discussion of Prntchk.ASM
  1140.  
  1141. When you use Prntchk with dBASE II  or pre-Developer's  Release versions of
  1142. dBASE III,  you RUN it or QUIT  TO it after assembling  it as a  .COM file.
  1143. This version of the program checks the printer status and does one of three
  1144. things:
  1145.  
  1146.      1. Reports that the printer is available, and returns control to dBASE
  1147.         II or dBASE III.
  1148.  
  1149.      2.  Reports that the printer is turned off, advises you to turn it on,
  1150.         and retains control until it is turned on.
  1151.  
  1152.      3.  Reports that the printer is off-line and retains control  until it
  1153.         is on-line.
  1154.  
  1155. Notice that  in  cases 2  and 3,  the program does not  relinquish control.
  1156. After issuing its report,  it waits for you to adjust the printer and press
  1157. any key to continue.   It will not release control until it obtains a ready
  1158. report from  the printer.   Thus it assures  that dBASE will not  attempt a
  1159. printing operation until the printer is  ready to carry  it out, protecting
  1160. your files from potential data loss.
  1161.  
  1162. The  program operates  somewhat  differently with  the Developer's Release.
  1163. Here,  it initializes a logical variable to  false (.F.),  then passes this
  1164. variable to Prntchk.   Prntchk checks the printer status, and places either
  1165. a 0  (.F.)  or a 1  (.T.)  back into the variable.  Upon return, your dBASE
  1166. program  can test  this  variable and  determine program flow  based on it.
  1167. Here is a short section of code  from a  printing program that is  based on
  1168. this idea.
  1169.  
  1170.      SET TALK OFF
  1171.      LOAD Prntchk
  1172.      ok2print = .F.
  1173.      DO WHILE .NOT. ok2print
  1174.         CALL Prntchk WITH ok2print
  1175.         IF .NOT. ok2print
  1176.            ?
  1177.            ? 'Printer is not ready - PLEASE CORRECT'
  1178.            WAIT
  1179.         ENDIF
  1180.      ENDDO
  1181.  
  1182. The program cannot get past this DO WHILE  loop until the  printer is ready
  1183. for output.
  1184.          .LFCOND
  1185.          PAGE    60,132
  1186.  ;
  1187.          D3DR    EQU     1               ; Assemble for dBASE III,
  1188.                                          ; Developer's Release.
  1189.          COM     EQU     0
  1190.          TRUE    EQU     1               ; Define symbols
  1191.          FALSE   EQU     0               ; for .T. and .F.
  1192.  ;**************************************
  1193.  CODESEG SEGMENT BYTE PUBLIC 'CODE'
  1194.          ASSUME CS:CODESEG,ES:CODESEG
  1195.  ;--------------------------------------
  1196.  PRNTCHK PROC    FAR
  1197.          IF      COM                     ; ORG at 100H
  1198.            ORG   100H                    ; for .COM file.
  1199.          ENDIF
  1200.  START:  JMP     NEXT_STEP               ; Skip past data.
  1201.  ;
  1202.  MESS1   DB      'PRINTER OFF-LINE - PLEASE ADJUST',0DH,0AH,'$'
  1203.  MESS2   DB      'PRINTER NOT TURNED ON - PLEASE TURN IT ON'
  1204.          DB      0DH,0AH,'$'
  1205.  MESS3   DB      'PRINTER AVAILABLE FOR PRINTING',0DH,0AH,'$'
  1206.  MESS4   DB      'PRESS ANY KEY TO CONTINUE',0DH,0AH,'$'
  1207.  OK      DB      ?
  1208.  ;
  1209.  NEXT_STEP:
  1210.          PUSH    AX              ; Save registers.
  1211.          PUSH    BX
  1212.          PUSH    DS
  1213.          PUSH    ES
  1214.          PUSH    CS
  1215.          POP     ES
  1216.          MOV     AX,40H          ; Point to system data segment.
  1217.          MOV     DS,AX
  1218.          MOV     SI,8            ; Point to LPT1 port address.
  1219.          MOV     DX,[SI]         ; Load it into DX.
  1220.          INC     DX              ; Point to LPT1 status port
  1221.          IN      AL,DX           ; and read it.
  1222.          CMP     AL,0DFH         ; Printer OK?
  1223.          JNE     OFF_LINE        ; No, is it off-line?
  1224.          MOV     ES:OK,TRUE      ; Place .T. in temporary variable.
  1225.          IF      COM
  1226.            MOV   DX,
  1227.  OFFSET MESS3                    ; Print report if .COM file.
  1228.            CALL  PRINTMESS
  1229.          ENDIF
  1230.          JMP     SHORT EXIT      ; Return to dBASE.
  1231.  OFF_LINE:
  1232.          CMP     AL,0CFH         ; Is printer off-line?
  1233.          JNE     TURNED_OFF      ; No, it must be turned off.
  1234.          MOV     ES:OK,FALSE     ; Place .F. in temporary variable.
  1235.          IF      COM
  1236.            MOV   DX,OFFSET MESS1 ; Print report if .COM file.
  1237.            CALL  PRINTMESS
  1238.            MOV   DX,OFFSET MESS4
  1239.            CALL  PRINTMESS
  1240.            CALL  CRLF            ; Skip line.
  1241.            POP   ES
  1242.            POP   DS
  1243.            POP   BX
  1244.            POP   AX
  1245.            CALL  WAIT            ; Wait for keypress.
  1246.            JMP   NEXT_STEP       ; Go back and check status again.
  1247.          ENDIF
  1248.          JMP     SHORT EXIT      ; Leave if OK.
  1249.  TURNED_OFF:
  1250.          MOV     ES:OK,FALSE     ; Place .F. in temporary variable.
  1251.          IF      COM
  1252.            MOV   DX,OFFSET MESS2 ; Print report if .COM file.
  1253.            CALL  PRINTMESS
  1254.            MOV   DX,OFFSET MESS4
  1255.            CALL  PRINTMESS
  1256.            CALL  CRLF            ; Skip line.
  1257.            POP   ES
  1258.            POP   DS
  1259.            POP   BX
  1260.            POP   AX
  1261.            CALL  WAIT            ; Wait for keypress.
  1262.            JMP   NEXT_STEP       ; Go back and check status again.
  1263.          ENDIF
  1264.  EXIT:   POP     ES              ; Restore registers.
  1265.          POP     DS              ; Now DS and BX are pointing
  1266.          POP     BX              ; to variable passed by
  1267.  dBASE III.
  1268.          IF      D3DR
  1269.            MOV   AL,ES:OK         ; Get .T. or .F. from temporary
  1270.                                   ; variable.
  1271.            MOV   BYTE PTR [BX],AL ; Place it in dBASE variable.
  1272.          ENDIF
  1273.          POP     AX              ; Restore remaining registers.
  1274.          IF      COM
  1275.            INT   20H             ; INT 20H if .COM file.
  1276.          ELSE
  1277.            RET                   ; Far return if Developer's
  1278.  Release.
  1279.          ENDIF
  1280.  ;
  1281.  PRNTCHK ENDP
  1282.  ;----------------------------------------
  1283.  ;            SUBROUTINES
  1284.  ;----------------------------------------
  1285.  CRLF    PROC    NEAR            ; Skips line.
  1286.          PUSH    AX              ; Save registers.
  1287.          PUSH    DX
  1288.          MOV     DL,0DH          ; Print carriage return.
  1289.          MOV     AH,2
  1290.          INT     21H
  1291.          MOV     DL,0AH          ; Print line-feed.
  1292.          MOV     AH,2
  1293.          INT     21H
  1294.          POP     DX              ; Restore registers.
  1295.          POP     AX
  1296.          RET                     ; Return to caller.
  1297.  CRLF    ENDP
  1298.  ;----------------------------------------
  1299.  PRINTMESS PROC  NEAR            ; Print message.
  1300.          PUSH    AX              ; Save registers.
  1301.          PUSH    DS
  1302.          MOV     AX,ES           ; ES points to Prntchk's
  1303.          MOV     DS,AX           ; data.
  1304.          XOR     AX,AX           ; Zero AX.
  1305.          MOV     AH,9            ; Call DOS print string function.
  1306.          INT     21H
  1307.          POP     DS              ; Restore registers.
  1308.          POP     AX
  1309.          RET                     ; Return to caller.
  1310.  PRINTMESS ENDP
  1311.  ;----------------------------------------
  1312.  WAIT    PROC    NEAR            ; Waits for keypress.
  1313.                                  ; Does not check for Ctrl-Break.
  1314.          PUSH    AX              ; Save register.
  1315.          MOV     AH,1            ; Call DOS, wait for keypres.
  1316.          INT     21H             ; Don't check Ctrl-Break
  1317.  function.
  1318.          POP     AX              ; Restore register.
  1319.          RET                     ; Return to caller.
  1320.  WAIT    ENDP
  1321.  ;----------------------------------------
  1322.  CODESEG ENDS
  1323.  ;******************************************
  1324.          END     START
  1325.  
  1326.  
  1327. 6  dBASE III Version 1.1 Change Summary
  1328.